{*******************************************************************************
* The contents of this file are used with permission, subject to the Mozilla   *
* Public License Version 1.1 (the "License"); you may not use this file except *
* in compliance with the License. You may obtain a copy of the License at      *
* http://www.mozilla.org/MPL/                                                  *
*                                                                              *
* Software distributed under the License is distributed on an "AS IS" basis,   *
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for *
* the specific language governing rights and limitations under the License.    *
*                                                                              *
* The Original Code is distributed as part of the "mediate" product and is     *
* Copyright (C) @home media limited. All Rights Reserved.                      *
*                                                                              *
* Email: support@athomemedia.co.uk                                             *
* Web:   http://www.athomemedia.co.uk                                          *
*******************************************************************************}

unit Video;

interface

uses Types, AHMTypes, AVRenderer, Canvas, Colour;

const
  VOLUME_SCALE = 100; // Scale factor to convert 0..-4000 DirectShow range to our 0..-40 range
  POSITION_SEEK_DELAY = 500; // Minimum seek delay period in milliseconds

type
  TAHMVideoRenderer = class(TAHMAVRenderer)
  protected
    function GetDuration: LongInt; override;
    function GetPosition: LongInt; override;
    procedure SetPosition(Position: LongInt); override;
    procedure SetVolume(Volume: Integer); override;
  public
    constructor Create;
    procedure UpdateMediaState; override;
    procedure UpdateRendererSettings; override;
    procedure Render(Rect: TAHMRectF; Shade: TAHMCanvasShade; Gamma: TAHMCanvasColour;
                     XScale, YScale: Single; XFlip, YFlip: Boolean); override;
    function Play: Boolean; override;
    function Pause: Boolean; override;
    function Stop: Boolean; override;
    function Mute: Boolean; override;
    function UnMute: Boolean; override;
    function FastForward: Boolean; override;
    function Rewind: Boolean; override;
    function SkipForward: Boolean; override;
    function SkipBackward: Boolean; override;
  end;

implementation

uses DirectXHelper, DirectShowManager, Timing;

constructor TAHMVideoRenderer.Create;
begin
  inherited;

  // Set position seeking interval for audio source
  FPositionInterval := POSITION_SEEK_DELAY;
end;

function TAHMVideoRenderer.GetDuration: LongInt;
begin
  with DirectShowInstance do
    if Assigned(DirectShowVideoRenderer) then
      Result := DirectShowVideoRenderer.Duration
    else
      Result := 0;
end;

function TAHMVideoRenderer.GetPosition: LongInt;
begin
  with DirectShowInstance do
    if Assigned(DirectShowVideoRenderer) then
      Result := DirectShowVideoRenderer.Position
    else
      Result := 0;
end;

procedure TAHMVideoRenderer.SetPosition(Position: LongInt);
begin
  // Inform ui timer that we're about to start rendering video
  TimerInstance.PreRequestRender;

  // Okay to update media position?
  if OkayToPositionMedia then
    with DirectShowInstance do
      if Assigned(DirectShowVideoRenderer) then
        DirectShowVideoRenderer.Seek(Position);
end;

procedure TAHMVideoRenderer.SetVolume(Volume: Integer);
begin
  inherited SetVolume(Volume);

  with DirectShowInstance do
    if Assigned(DirectShowVideoRenderer) then
      DirectShowVideoRenderer.InternalVolume := Volume * VOLUME_SCALE;
end;

procedure TAHMVideoRenderer.UpdateMediaState;
begin
  with DirectShowInstance do
    if Assigned(DirectShowVideoRenderer) then
    begin
      // Check for any outstanding direct show events
      DirectShowVideoRenderer.CheckEventQueue;

      // Only really interested in end of file event - signal next item
      if DirectShowVideoRenderer.EndOfFile then SignalMediaEvent(msigNext);
    end;
end;

procedure TAHMVideoRenderer.UpdateRendererSettings;
begin
  with DirectShowInstance do
    if Assigned(DirectShowVideoRenderer) then
    begin
      DirectShowVideoRenderer.UpdateAudioDSPSettings;
    end;
end;

procedure TAHMVideoRenderer.Render(Rect: TAHMRectF; Shade: TAHMCanvasShade;
                                   Gamma: TAHMCanvasColour; XScale, YScale: Single;
                                   XFlip, YFlip: Boolean);
begin
  inherited;

  // Determine whether we're running video fullscreen
  with DirectShowInstance do
    if Assigned(DirectShowVideoRenderer) then DirectShowVideoRenderer.Fullscreen := FFullscreen;

  if MediaState = msStopped then Exit;

  // Delegate this task to DirectX layer
  DirectXInstance.DrawVideoFrame(Rect, BackgroundColour, Shade, Gamma, XScale, YScale, XFlip, YFlip);
end;

function TAHMVideoRenderer.Play: Boolean;
begin
  // Set default result - couldn't play
  Result := False;

  if Assigned(Playlist.CurrentItem) then
  begin
    with DirectShowInstance do
    begin
      // Check if video renderer is currently in use
      if Assigned(DirectShowVideoRenderer) then
      begin
        // In use - if we own it then just resume playback
        if DirectShowVideoRenderer.Owner = Self then
          DirectShowVideoRenderer.Play
        else
        begin
          // Owned by other stream, so nick it, but inform other stream first
          TAHMAVRenderer(DirectShowVideoRenderer.Owner).SignalMediaEvent(msigAbort);
          StopVideo;
        end;
      end;

      // Check if video renderer needs initialising
      if not Assigned(DirectShowVideoRenderer) then
      begin
        if not PlayVideo(Playlist.CurrentItem.URL) then
        begin
          // Map DirectX video error into one of our generic error types
          case DirectShowInstance.LastVideoError of
            dvOkay:         LastError := meNone;
            dvNoDirectShow: LastError := meInstallationBroken;
            dvNoVMR:        LastError := meUnsupportedHardware;
            dvMissingCodec: LastError := meUnsupportedMedia;
            dvBrokenCodec:  LastError := meExternalError;
          end;
          Exit;
        end;

        DirectShowVideoRenderer.InternalVolume := Volume * VOLUME_SCALE;
        if Muted then DirectShowVideoRenderer.InternalFastMute;
        DirectShowVideoRenderer.Owner := Self;
      end;

      // Inform ui timer that we're about to start rendering video
      TimerInstance.PreRequestRender;

      Result := True;
    end;
  end;
end;

function TAHMVideoRenderer.Pause: Boolean;
begin
  // Set default result - couldn't pause
  Result := False;

  with DirectShowInstance do
    if Assigned(DirectShowVideoRenderer) then
    begin
      DirectShowVideoRenderer.Pause;

      // Resume fullspeed UI rendering
      TimerInstance.CancelRenderRequests;

      Result := True;
    end;
end;

function TAHMVideoRenderer.Stop: Boolean;
begin
  // Stop video stream
  DirectShowInstance.StopVideo;

  // Resume fullspeed UI rendering
  TimerInstance.CancelRenderRequests;

  Result := True;
end;

function TAHMVideoRenderer.Mute: Boolean;
begin
  // Set default result - couldn't mute
  Result := False;

  with DirectShowInstance do
    if Assigned(DirectShowVideoRenderer) then
      Result := DirectShowVideoRenderer.InternalFastMute;
end;

function TAHMVideoRenderer.UnMute: Boolean;
begin
  // Set default result - couldn't unmute
  Result := False;

  with DirectShowInstance do
    if Assigned(DirectShowVideoRenderer) then
      Result := DirectShowVideoRenderer.InternalFastUnMute;
end;

function TAHMVideoRenderer.FastForward: Boolean;
var
  Position: LongInt;
begin
  // Set default result - can't fast forward
  Result := False;

  // Inform ui timer that we're about to start rendering video
  TimerInstance.PreRequestRender;

  // Okay to update media position?
  if not OkayToPositionMedia then
  begin
    // We recently updated media position, so give DirectShow a chance to
    // catch up before we make another request. However, we'll return a true
    // result to prevent media manager from delegating the request to the
    // other media stream.
    Result := True;
  end
  else
    with DirectShowInstance do
      if Assigned(DirectShowVideoRenderer) then
      begin
        Position := DirectShowVideoRenderer.Position;
        Position := Position + DEFAULT_FWD_SKIP;
        if Position > DirectShowVideoRenderer.Duration then
          Position := DirectShowVideoRenderer.Duration;
        Result := DirectShowVideoRenderer.Seek(Position);
      end;
end;

function TAHMVideoRenderer.Rewind: Boolean;
var
  Position: LongInt;
begin
  // Set default result - can't rewind
  Result := False;

  // Inform ui timer that we're about to start rendering video
  TimerInstance.PreRequestRender;

  // Okay to update media position?
  if not OkayToPositionMedia then
  begin
    // We recently updated media position, so give DirectShow a chance to
    // catch up before we make another request. However, we'll return a true
    // result to prevent media manager from delegating the request to the
    // other media stream.
    Result := True;
  end
  else
    with DirectShowInstance do
      if Assigned(DirectShowVideoRenderer) then
      begin
        Position := DirectShowVideoRenderer.Position;
        Position := Position - DEFAULT_BWD_SKIP;
        if Position < 0 then Position := 0;
        Result := DirectShowVideoRenderer.Seek(Position);
      end;
end;

function TAHMVideoRenderer.SkipForward: Boolean;
begin
  // Stop current file & play new item
  Stop;
  Result := Play;
end;

function TAHMVideoRenderer.SkipBackward: Boolean;
begin
  // Stop current file & play new item
  Stop;
  Result := Play;
end;


end.
